package com.dazhi.boot;

import com.dazhi.core.io.ResourceLoader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver;
import org.springframework.util.ClassUtils;

import java.util.*;
import java.util.stream.Stream;

public class SpringApplication {
    private ResourceLoader resourceLoader;
    ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
    private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
    private final Set<Class<?>> primarySources;
    private final List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
    private List<ApplicationContextInitializer<?>> initializers;
    private List<ApplicationListener<?>> listeners;
    private Class<?> mainApplicationClass;
    private static final Log logger = LogFactory.getLog(SpringApplication.class);

    // 添加构造方法
    public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;   //初始化是个null
        // 主要的源
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 正经的啥也加载不到
        this.bootstrapRegistryInitializers = new ArrayList<>(
                getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        // 初始化ApplicationContextInitializer
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 初始化ApplicationListener
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 一个神奇的寻找主类的方法
        this.mainApplicationClass = deduceMainApplicationClass();
    }

    public static void run(Class<?> primarySource, String[] args) {
        run(new Class[]{primarySource}, args);
    }

    private static void run(Class[] primarySources, String[] args) {
        new SpringApplication(primarySources).run(args);
    }
    // 开始启动
    public ConfigurableApplicationContext run(String... args) {
        // 初始化容器，new一个DefaultBootstrapContext
        // 初始化BootstrapContext
        ConfigurableBootstrapContext bootstrapContext = new DefaultBootstrapContext();
        // 添加监听，通过springframework读取spring.factories中的接口实现类初始化，正经的EventPublishingRunListener,用来发布事件
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 1 starting 只初始化了logging日志
        listeners.starting(bootstrapContext);
        // 创建一个environment，applicationServletEnvironment,配置必要的参数
        ConfigurableEnvironment environment = prepareEnvironment();
        // 2 environmentPrepared,应该是加载启动是命令行里的配置项
        listeners.environmentPrepared(bootstrapContext, environment);
        // 将默认配置放到最下面
        // 绑定环境变量
        // 3 contextPrepared
        ConfigurableApplicationContext context = createApplicationContext();
        preparedContext();
        listeners.contextPrepared(context);
        // 4 contextLoaded
        contextLoaded(context);
        listeners.contextLoaded(context);
        // 5 started
        refreshContext(context);
        listeners.started(context, null);
        // 6 ready
        callRunners(context);
        try {
            listeners.ready(context, null);
        }catch (Throwable th){
            // 7 failed
            listeners.failed(context, null);
        }
        return context;
    }

    private void preparedContext() {
//        postProcessApplicationContext(context);
//        addAotGeneratedInitializerIfNecessary(this.initializers);
//        applyInitializers(context);
    }

    private void contextLoaded(ConfigurableApplicationContext context) {
//        bootstrapContext.close(context);
//        // Add boot specific singleton beans
//        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);

    }

    private void callRunners(ConfigurableApplicationContext context) {
    }

    private void refreshContext(ConfigurableApplicationContext context) {
        // 刷新缓存，启动tomcat
        context.refresh();
    }

    private ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create();
    }

    private ConfigurableEnvironment prepareEnvironment() {
        return null;
    }

    // 通过factories文件加载Spring的实现类SpringApplicationRunListener
    private SpringApplicationRunListeners getRunListeners(String[] args) {
        ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
        argumentResolver = argumentResolver.and(String[].class, args);
        List<SpringApplicationRunListener> listeners = SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(SpringApplicationRunListener.class,argumentResolver);
        return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
    }
    public ClassLoader getClassLoader() {
        if (this.resourceLoader != null) {
            return this.resourceLoader.getClassLoader();
        }
        return ClassUtils.getDefaultClassLoader();
    }

    private <T> List<T> getSpringFactoriesInstances(Class<T> type) {
        return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, (ArgumentResolver) null);
    }

    public List<ApplicationListener<?>> getListeners() {
        return listeners;
    }

    public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
        this.listeners = new ArrayList<>(listeners);
    }

    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList<>(initializers);
    }
    private Class<?> deduceMainApplicationClass() {
        return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
                .walk(this::findMainClass)
                .orElse(null);
    }

    private Optional<Class<?>> findMainClass(Stream<StackWalker.StackFrame> stack) {
        return stack.filter((frame) -> Objects.equals(frame.getMethodName(), "main"))
                .findFirst()
                .map(StackWalker.StackFrame::getDeclaringClass);
    }
}
